package {
	import flash.geom.ColorTransform;
	import flash.utils.ByteArray;
	import org.flixel.FlxSound;
	import flash.events.TimerEvent;
	import flash.utils.Timer;
	import org.flixel.FlxText;
	import jam.registry.Resource;
	import org.flixel.FlxG;
	import org.flixel.FlxSprite;

	public class HeartRateMonitor extends FlxSprite {
		
		private const BORDER_COLOR:int = 0xffffffff;
		private const BORDER_WIDTH:int = 5;
		
		private var blips:Array;
		private var addFunction:Function;
		private var removeFunction:Function;
		private var onMenu:Boolean;
		
		private var blipIndex:int = 0;
		private var blipLength:int = 38;
		
		private var beating:Boolean = false;
		private var beatIndex:int = 0;
		private var beatGap:Number;
		
		private var elapsedTime:Number = 0;
		private var elapsedBeatTime:Number = 0;
		
		private var beatTimer:Timer;
		private var beatSoundState:FlxSound;
		private var flatlining:Boolean;
		private var flatlineSoundState:FlxSound;
		
		private var bpm:Number;
		
		private var debug:FlxText;
		
		public function HeartRateMonitor(x:Number, y:Number, 
											width:Number, height:Number,
											addFunction:Function, removeFunction:Function,
											onMenu:Boolean=false) {
			super(x, y);
			this.width = width;
			this.height = height;
			this.addFunction = addFunction;
			this.removeFunction = removeFunction;
			this.onMenu = onMenu;
			create();
			if (Game.DEBUG)
			{
				debug = new FlxText(0, 200, 320);
				debug.color = 0xffffffff;
				debug.size = 12;
				debug.scrollFactor.x = debug.scrollFactor.y = 0;
				addFunction(debug);
			}
		}
		
		private function create():void
		{
			this.solid = false;
			
			blipLength = width / 5 - 2;
			blips = new Array(blipLength);
			
			createTimer();
			
			//back
			this.makeGraphic(width, height, 0xff000000);
			
			if (!onMenu)
			{
				//border
				this.drawLine(x - BORDER_WIDTH * 2, y - BORDER_WIDTH * 2,
								x + width - BORDER_WIDTH * 2, y - BORDER_WIDTH * 2,
								BORDER_COLOR, BORDER_WIDTH);
				this.drawLine(x - BORDER_WIDTH * 2, y + height - BORDER_WIDTH * 2,
								x + width - BORDER_WIDTH * 2, y + height - BORDER_WIDTH * 2,
								BORDER_COLOR, BORDER_WIDTH);
				this.drawLine(x - BORDER_WIDTH * 2, y - BORDER_WIDTH * 2,
								x - BORDER_WIDTH * 2, y + height - BORDER_WIDTH * 2,
								BORDER_COLOR, BORDER_WIDTH);
				this.drawLine(x + width - BORDER_WIDTH * 2, y - BORDER_WIDTH * 2,
								x + width - BORDER_WIDTH * 2, y + width - BORDER_WIDTH * 2,
								BORDER_COLOR, BORDER_WIDTH);
			}
			
			beatSoundState = new FlxSound();
			beatSoundState.loadEmbedded(Resource.SOUND_HEART_MONITOR);
			beatSoundState.volume = 0.5;
		}
		
		private function createTimer():void
		{
			beatTimer = new Timer(500);
			beatTimer.addEventListener(TimerEvent.TIMER, beatHandler);
			beatTimer.start();
		}
		
		public function start():void
		{
			flatlining = false;
			beatTimer.start();
			if (flatlineSoundState)
				flatlineSoundState.stop();
		}
		
		public function stop(fade:Number=0):void
		{
			flatlining = true;
			beatTimer.stop();
			beatSoundState.stop();
			flatlineSoundState = FlxG.play(Resource.SOUND_FLATLINE, 0.75);
			if (fade > 0)
			{
				flatlineSoundState.fadeOut(fade);
			}
		}
		
		private function beatHandler(event:TimerEvent):void
		{
			beating = true;
			beatIndex = 0;
			beatTimer.delay = 60 / bpm * 1000;
			beatSoundState.play(true);
		}
		
		public function setBPM(bpm:Number):void
		{
			this.bpm = bpm;
			var mid:Number = (Player.MAX_HEART_RATE + Player.MIN_HEART_RATE) / 2;
			beatSoundState.volume = Math.max(0.1, Math.abs(bpm - mid) / mid);
			
			if (Game.DEBUG)
			{
//				debug.text = "mid " + mid + " vol " + beatSoundState.volume;
				debug.text = bpm.toFixed();
			}
		}
		
		override public function update():void
		{
			super.update();
			
			if (!onMenu) {
				if (flatlining) {
					this.replaceColor(0xFF000000, 0xFFFF0000);
					this.replaceColor(0xFFCC0000, 0xFFFF0000);
				}
				else if (bpm >= Player.MAX_HEART_RATE - Player.BUMP_SIZE) {
					this.replaceColor(0xFF000000, 0xFFCC0000);
					this.replaceColor(0xFFFF0000, 0xFFCC0000);
				}
				else if (bpm < Player.MIN_HEART_RATE + Player.BUMP_SIZE - 15) {
					this.replaceColor(0xFF000000, 0xFFCC0000);
					this.replaceColor(0xFFCC0000, 0xFFCC0000);
				}
				else {
					this.replaceColor(0xFFCC0000, 0xFF000000);
					this.replaceColor(0xFF000000, 0xFF000000);
				}
			}
			
			elapsedTime += FlxG.elapsed;
			
			if (elapsedTime > 0.1)
			{
				// update heartbeat monitor	
				elapsedTime = 0;
				
				//erase the slot ahead
				var removeIndex:int = (blipIndex + 1) % blipLength;
					removeFunction(blips[removeIndex]);
					blips[removeIndex] = null;
				
				//add the current blip
				var newBlip:FlxSprite = new FlxSprite();
				newBlip.x = blipIndex * 5 + x + 5;
				newBlip.scrollFactor.x = newBlip.scrollFactor.y = 0;
				newBlip.solid = false;
				if (beating)
				{
					switch (beatIndex++)
					{
						case 0:
							newBlip.y = y + height / 2 - height / 4;
							newBlip.makeGraphic(5, height / 3.33, 0xff00ff00);
							break;
						case 1:
							newBlip.y = y + height / 2 - height / 2.85;
							newBlip.makeGraphic(5, height / 10, 0xff00ff00);
							break;
						case 2:
							newBlip.y = y + height / 2 - height / 4;
							newBlip.makeGraphic(5, height / 2, 0xff00ff00);
							break;
						case 3:
							newBlip.y = y + height / 2 + height / 4;
							newBlip.makeGraphic(5, height / 10, 0xff00ff00);
							break;
						case 4:
							newBlip.y = y + height / 2 - height / 20;
							newBlip.makeGraphic(5, height / 3.33, 0xff00ff00);
							beatIndex = 0;
							beating = false;
							break;
					}
				}
				else
				{
					newBlip.y = y + height / 2 - height / 20;
					newBlip.makeGraphic(5, height / 10, 0xff00ff00);
				}
				
				blips[blipIndex] = newBlip;
				addFunction(newBlip);
				
				blipIndex++;
				if (blipIndex == blipLength)
					blipIndex = 0;
			}
		}
	}
}
